<?php

declare(strict_types=1);

/**
 * @Author:     Frank
 * @contact     frank_hhm@163.com
 * @Date:       2023-02-03 09:10:55
 * @Last Modified time: 2023-02-18 15:06:35
 */

namespace app\common\services\store\order;

use frank\base\BaseService;
use frank\exception\CommonException;
use frank\utils\helper\ArrayHelper;
use frank\utils\helper\StringHelper;

use app\common\enum\store\order\SourceEnum;

use app\common\dao\store\ShippingTemplatesDao;

/**
 * 配送服务类
 * Class ExpressService
 */
class ExpressService extends BaseService
{	
	/**
	 * 用户收货城市id
	 */
	private $cityId;

	/**
	 * 订单商品列表
	 */
    private $goodsList;

    /**
     * 订单来源 (主商城)
     */
    private $OrderSource;

    /**
     * 不在配送范围的商品ID
     */
    private $notInRegionGoodsId;

    /**
     * 运费模板数据集
     */
    private $data = [];

    private $express = [
    	'price'=>0.00,				//配送金额
    	'is_express'=>0,			//配送状态
    	'error'=>'',				//不配送理由
    	'is_free'=>0, 				//是否包邮
    ];

    /**
     * 固定邮费
     */
    private $fixedFree = [];

    /**
     * 构造方法
     */
    public function __construct(ShippingTemplatesDao $dao,$cityId, $goodsList, $OrderSource = SourceEnum::MASTER)
    {
    	$this->dao = $dao;
    	// 赋值传参
        $this->cityId = $cityId;
        $this->goodsList = $goodsList;
        $this->OrderSource = $OrderSource;
        // 整合运费模板
        $this->initExpressTemplate();
        $this->initFixedFree();
    }

    /**
     * 整合运费模板
     */
    private function initExpressTemplate()
    {
        // 运费模板ID集
        $templateIds = ArrayHelper::getArrayColumn($this->goodsList, 'delivery_shipping_temp_id');
        // 运费模板列表
        $templateList = $this->dao->search(['id'=>array_values(array_unique($templateIds))])->with(['citys','freeCitys'])->select()->toArray();
        // 整理数据集
        foreach ($templateList as $item) {
            $this->data[$item['id']]['delivery_express'] = $item;
            $this->data[$item['id']]['goodsList'] = $this->getGoodsListByTempId($item['id']);
        }
    }

    /**
     * 固定邮费商品
     */
    public function initFixedFree(){
    	foreach ($this->goodsList as $key => &$item) {
    		if($item['delivery_shipping']['value'] == 2){
    			$item['express_price'] = StringHelper::number2($item['delivery_shipping_price']);
    			$this->fixedFree[] = $item['express_price'];
    		}
    	}
    }

    /**
     * 根据运费模板id整理商品集
     */
    private function getGoodsListByTempId($tempId)
    {
        $data = [];
        foreach ($this->goodsList as $item) {
            if($item['delivery_shipping']['value'] == 3){
                $item['delivery_shipping_temp_id'] == $tempId && $data[] = $item;
            }
        }
        return $data;
    }

    /**
     * 验证用户收货地址是否在配送范围
     */
    public function isIntraRegion()
    {
        if (!$this->cityId) return 0;
        
        foreach ($this->data as $item) {
            $cityIds = ArrayHelper::getArray2Column($item['delivery_express']['citys'],'citys');
        	if (!in_array($this->cityId, $cityIds)) {
                $this->notInRegionGoodsId = current($item['goodsList'])['id'];
                continue;
            }
        }
        return 1;
    }

    /**
     * 获取不在配送范围的商品名称
     */
    public function getNotInRegionGoodsName()
    {
        $item = ArrayHelper::getArrayItemByColumn($this->goodsList, 'id', $this->notInRegionGoodsId);
        return !empty($item) ? $item['goods_name'] : null;
    }

    /**
     * 获取订单的配送费用
     */
    public function getExpress()
    {
    	$this->express['is_express'] = $this->isIntraRegion();
    	if($this->notInRegionGoodsId > 0){
    		$this->express['error'] = "很抱歉，您的收货地址不在商品 [{$this->getNotInRegionGoodsName()}] 的配送范围内";
    	}
        if (empty($this->goodsList) || $this->notInRegionGoodsId > 0) {
        	// 无法配送
            $this->express['price'] = 0.00;
        }elseif(sysconf('store_delivery_is_free') == 1 && StringHelper::number2(ArrayHelper::getArrayColumnSum($this->goodsList, 'total_price')) >= sysconf('store_delivery_is_free_price')){
        	// 全场满额包邮
        	$this->express['price'] = 0.00;
        }else{
	        // 计算配送金额
	        $arr = [];
	        foreach ($this->data as &$item) {
	            // 计算当前配送模板的运费
	            $item['express_price'] = $this->calcDeliverAmount($item);
	        }
	        // 根据运费组合策略获取最终运费金额
	        $this->express['price'] = StringHelper::number2($this->getFinalFreight());
        }
        if($this->express['price'] == 0){
        	$this->express['is_free'] = 1;
        }
        return $this->express;
    }

    /**
     * 根据运费组合策略 计算最终运费
     */
    private function getFinalFreight()
    {
        // 运费合集
        $expressPriceArr = array_merge($this->fixedFree,ArrayHelper::getArrayColumn($this->data, 'express_price'));
        // 最终运费金额
        $expressPrice = 0.00;
        if(empty($expressPriceArr)) return $expressPrice;
        // 判断运费组合策略
        switch (sysconf('store_delivery_rule')) {
            case 1:    // 策略1: 叠加
                $expressPrice = array_sum($expressPriceArr);
                break;
            case 2:    // 策略2: 以最低运费结算
                $expressPrice = min($expressPriceArr);
                break;
            case 3:    // 策略3: 以最高运费结算
                $expressPrice = max($expressPriceArr);
                break;
        }
        return $expressPrice;
    }

    /**
     * 计算当前配送模板的运费
     */
    private function calcDeliverAmount($item)
    {
        // 获取运费模板下商品总数量or总重量
        $totality = $this->getItemGoodsTotal($item);
    	// 是否在指定包邮区域
    	$deliverFree = $this->isCityExpressyFee($item['delivery_express']);
    	if($deliverFree && (
    			$totality >= $deliverFree['number'] // 满模板免邮 件
    			|| StringHelper::number2(ArrayHelper::getArrayColumnSum($item['goodsList'], 'total_price')) >= $deliverFree['money']   // 满模板免邮 金额
    		)){
    			return 0.00;
    	}
		 // 当前收货城市配送规则
        $deliverRule = $this->getCityExpressyRule($item['delivery_express']);

        if ($totality <= $deliverRule['first']) {
           return  $deliverRule['first_money'];
        }

         // 续件or续重 数量
        $addNumber = $totality - $deliverRule['first'];
        if ($addNumber <= $deliverRule['add_number']) {
             return StringHelper::bcadd($deliverRule['first_money'], $deliverRule['add_money']);
        }
        // 计算续重/件金额
        $addMoney = $deliverRule['add_number'] < 1 ? 0.00 : StringHelper::bcdiv($deliverRule['add_money'], $deliverRule['add_number']) * $addNumber;
    	return StringHelper::bcadd($deliverRule['first_money'], $addMoney);

    }

    /**
     * 获取运费模板下商品总数量or总重量
     */
    private function getItemGoodsTotal($item)
    {
        $totalWeight = 0;   // 总重量
        $totalNum = 0;      // 总数量
        foreach ($item['goodsList'] as $goodsItem) {
            $goodsWeight = StringHelper::bcmul($goodsItem['goods_sku']['weight'], $goodsItem['total_num']);
            $totalWeight = StringHelper::bcadd($totalWeight, $goodsWeight);
            $totalNum = StringHelper::bcadd($totalNum, $goodsItem['total_num']);
        }
        return $item['delivery_express']['type']['value'] === 1 ? (int)$totalNum :  (int)$totalWeight;
    }

    /**
     * 指定包邮区域处理
     */
    public function isCityExpressyFee($express){
    	$itemRule = false;
        foreach ($express['freeCitys'] as $item) {
            if (in_array($this->cityId, $item['citys'])) {
                $itemRule = $item;
                continue;
            }
        }
        return $itemRule;
    }

    /**
     * 根据城市id获取规则信息
     */
    private function getCityExpressyRule($express)
    {	
    	$itemRule = false;
    	// 默认配送区域
        foreach ($express['citys'] as $item) {
            if (in_array($this->cityId, $item['citys'])) {
                $itemRule = $item;
                continue;
            }
        }
        return $itemRule;
    }
}
